#ifndef AST_H
#define AST_H

#include <stdint.h>
#include <vector>
#include <boost/shared_ptr.hpp>
#include "token.h"
#include "errors.h"

class Package;
class Module;

class AST : public Location
{
public:
   AST( const Location& loc ) : Location( loc ) {}
	virtual ~AST() {}

   virtual void print( uint32_t level ) const;
};

class ASTBaseType : public AST
{
public:
   typedef boost::shared_ptr<ASTBaseType> Ptr;

protected:
   std::vector<std::string> _list;

public:
   ASTBaseType( const Token& token );

   virtual uint32_t count() const { return _list.size(); }
   virtual const std::string& get( uint32_t i ) const { return _list[i]; }
   virtual void append( const Token& token ) { _list.push_back( token.string() ); }
   virtual void print_name() const;
   virtual void print( uint32_t level ) const;
};

class ASTType : public AST
{
public:
   typedef boost::shared_ptr<ASTType> Ptr;
   typedef std::vector<Ptr> Vector;

   typedef enum
   {
      REF_STRONG,
      REF_NIL,
      REF_WEAK
   } ref_t;

protected:
   ASTBaseType::Ptr _base;
   ASTType::Vector _parameters;
   ref_t _ref;

public:
   ASTType( ASTBaseType::Ptr base );

   virtual const ASTBaseType::Ptr base() const { return _base; }
   virtual void bind( ASTType::Ptr parameter );
   virtual ref_t ref() const { return _ref; }
   virtual void set_ref( ref_t ref );
   virtual void print_name() const;
   virtual void print( uint32_t level ) const;
};

class ASTTypeDefinition : public AST
{
public:
   typedef boost::shared_ptr<ASTTypeDefinition> Ptr;
   typedef std::vector<Ptr> Vector;

protected:
   std::string _name;
   ASTType::Ptr _base;
   ASTTypeDefinition::Ptr _base_def;

public:
   ASTTypeDefinition( const Token& name );

   virtual const std::string& name() const { return _name; }
   virtual const ASTType::Ptr base() const { return _base; }
   virtual void set_base( ASTType::Ptr base );
   virtual void set_base_def( ASTTypeDefinition::Ptr def );

   virtual void shadow_check( Errors& /*errors*/ ) {}
   virtual void type_check( Module& /*module*/, Errors& /*errors*/ ) {}
};

class ASTImport : public AST
{
public:
   typedef boost::shared_ptr<ASTImport> Ptr;
   typedef std::vector<Ptr> Vector;

protected:
   ASTBaseType::Ptr _type;
   ASTTypeDefinition::Ptr _type_def;

public:
   ASTImport( ASTBaseType::Ptr type );

   virtual const ASTBaseType::Ptr name() const { return _type; }
   virtual const ASTTypeDefinition::Ptr type() const { return _type_def; }
   virtual void set_type( ASTTypeDefinition::Ptr type );
   virtual bool match( const std::string& name ) const;
   virtual void print( uint32_t level ) const;
};

class ASTExpr : public AST
{
public:
   typedef boost::shared_ptr<ASTExpr> Ptr;
   typedef std::vector<Ptr> Vector;
   typedef boost::shared_ptr<Vector> VectorPtr;

   ASTExpr( const Location& loc ) : AST( loc ) {}
};

class ASTBoolean : public ASTExpr
{
protected:
   bool _value;

public:
   ASTBoolean( const Token& token );
   virtual void print( uint32_t level ) const;
};

class ASTInteger : public ASTExpr
{
protected:
   int64_t _value;

public:
   ASTInteger( const Token& token );
   virtual void print( uint32_t level ) const;
};

class ASTReal : public ASTExpr
{
protected:
   double _value;

public:
   ASTReal( const Token& token );
   virtual void print( uint32_t level ) const;
};

class ASTImaginary : public ASTExpr
{
protected:
   double _value;

public:
   ASTImaginary( const Token& token );
   virtual void print( uint32_t level ) const;
};

class ASTString : public ASTExpr
{
protected:
   std::string _value;

public:
   ASTString( const Token& token );
   virtual void print( uint32_t level ) const;
};

class ASTArray : public ASTExpr
{
public:
   typedef boost::shared_ptr<ASTArray> Ptr;

protected:
   ASTExpr::VectorPtr _list;

public:
   ASTArray( const Token& token ) : ASTExpr( token ) {}

   virtual void set_list( ASTExpr::VectorPtr list );
   virtual void print( uint32_t level ) const;
};

class ASTThis : public ASTExpr
{
public:
   ASTThis( const Token& token ) : ASTExpr( token ) {}
   virtual void print( uint32_t level ) const;
};

class ASTIdentifier : public ASTExpr
{
public:
   typedef boost::shared_ptr<ASTIdentifier> Ptr;
   typedef std::vector<Ptr> Vector;

protected:
   std::string _name;

public:
   ASTIdentifier( const Token& token );

   virtual const std::string& name() const { return _name; }
   virtual void print( uint32_t level ) const;
};

class ASTUnary : public ASTExpr
{
public:
   typedef boost::shared_ptr<ASTUnary> Ptr;

protected:
   Token::type_t _type;
   ASTExpr::Ptr _rhs;

public:
   ASTUnary( const Token& token );

   virtual void set_rhs( ASTExpr::Ptr rhs );
   virtual void print( uint32_t level ) const;
};

class ASTBinary : public ASTUnary
{
public:
   typedef boost::shared_ptr<ASTBinary> Ptr;

protected:
   ASTExpr::Ptr _lhs;

public:
   ASTBinary( const Token& token, ASTExpr::Ptr lhs );
   virtual void print( uint32_t level ) const;
};

class ASTArrayIndex : public ASTExpr
{
protected:
   ASTExpr::Ptr _array;
   ASTExpr::VectorPtr _dimensions;

public:
   ASTArrayIndex( ASTExpr::Ptr array, ASTExpr::VectorPtr dimensions );
   virtual void print( uint32_t level ) const;
};

class ASTCall : public ASTExpr
{
public:
   typedef boost::shared_ptr<ASTCall> Ptr;

protected:
   ASTExpr::Ptr _method;
   ASTExpr::VectorPtr _args;

public:
   ASTCall( ASTExpr::Ptr method );

   virtual void set_args( ASTExpr::VectorPtr args );
   virtual void print( uint32_t level ) const;
};

class ASTSuperCall : public ASTCall
{
public:
   ASTSuperCall( ASTExpr::Ptr method ) : ASTCall( method ) {}
   virtual void print( uint32_t level ) const;
};

class ASTObjectCall : public ASTCall
{
protected:
   ASTExpr::Ptr _object;

public:
   ASTObjectCall( ASTExpr::Ptr object, ASTExpr::Ptr method );
   virtual void print( uint32_t level ) const;
};

class ASTTypeIdentifier : public ASTExpr
{
public:
   typedef boost::shared_ptr<ASTTypeIdentifier> Ptr;
   typedef std::vector<Ptr> Vector;
   typedef boost::shared_ptr<Vector> VectorPtr;

protected:
   ASTType::Ptr _type;
   ASTIdentifier::Ptr _identifier;

public:
   ASTTypeIdentifier( ASTType::Ptr type );

   virtual void set_identifier( ASTIdentifier::Ptr identifier );
   virtual void print( uint32_t level ) const;
};

class ASTCommand : public AST
{
public:
   typedef boost::shared_ptr<ASTCommand> Ptr;
   typedef std::vector<Ptr> Vector;

   ASTCommand( const Location& loc ) : AST( loc ) {}
};

class ASTVariable : public ASTCommand
{
public:
   typedef boost::shared_ptr<ASTVariable> Ptr;

protected:
   ASTType::Ptr _type;
   ASTIdentifier::Vector _identifiers;
   ASTExpr::VectorPtr _initialiser;

public:
   ASTVariable( ASTType::Ptr type );

   virtual void add_identifier( ASTIdentifier::Ptr id );
   virtual void set_initialiser( ASTExpr::VectorPtr initialiser );
   virtual void print( uint32_t level ) const;
};

class ASTExprCommand : public ASTCommand
{
protected:
   ASTExpr::VectorPtr _list;

public:
   ASTExprCommand( ASTExpr::VectorPtr list );
   virtual void print( uint32_t level ) const;
};

class ASTAssign : public ASTCommand
{
public:
   typedef boost::shared_ptr<ASTAssign> Ptr;

protected:
   ASTExpr::VectorPtr _lhs;
   ASTExpr::VectorPtr _rhs;

public:
   ASTAssign( ASTExpr::VectorPtr lhs );
   virtual void set_rhs( ASTExpr::VectorPtr rhs );
   virtual void print( uint32_t level ) const;
};

class ASTBlock : public ASTCommand
{
public:
   typedef boost::shared_ptr<ASTBlock> Ptr;

protected:
   ASTCommand::Vector _commands;

public:
   ASTBlock( const Token& token ) : ASTCommand( token ) {}

   virtual bool empty() const { return _commands.empty(); }
   virtual void add_command( ASTCommand::Ptr command );
   virtual void print( uint32_t level ) const;
};

class ASTIf : public ASTCommand
{
public:
   typedef boost::shared_ptr<ASTIf> Ptr;

protected:
   ASTExpr::Ptr _condition;
   ASTBlock::Ptr _on_true;
   ASTBlock::Ptr _on_false;

public:
   ASTIf( ASTExpr::Ptr condition );

   virtual void set_true( ASTBlock::Ptr on_true );
   virtual void set_false( ASTBlock::Ptr on_false );
   virtual void print( uint32_t level ) const;
};

class ASTReturn : public ASTCommand
{
public:
   ASTReturn( const Token& token ) : ASTCommand( token ) {}
   virtual void print( uint32_t level ) const;
};

class ASTLiteral : public AST
{
public:
   typedef boost::shared_ptr<ASTLiteral> Ptr;
   typedef std::vector<Ptr> Vector;

protected:
   ASTIdentifier::Ptr _name;
   ASTExpr::Ptr _value;

public:
   ASTLiteral( ASTIdentifier::Ptr name );

   virtual const ASTIdentifier::Ptr identifier() const { return _name; }
   virtual void set_value( ASTExpr::Ptr value );
   virtual void print( uint32_t level ) const;
};

class ASTMember : public AST
{
public:
   typedef boost::shared_ptr<ASTMember> Ptr;
   typedef std::vector<Ptr> Vector;

protected:
   ASTType::Ptr _type;
   ASTIdentifier::Ptr _name;

public:
   ASTMember( ASTType::Ptr type );

   virtual const ASTType::Ptr type() const { return _type; }
   virtual const ASTIdentifier::Ptr identifier() const { return _name; }
   virtual void set_identifier( ASTIdentifier::Ptr id );
   virtual void print( uint32_t level ) const;
};

class ASTMethod : public AST
{
public:
   typedef boost::shared_ptr<ASTMethod> Ptr;
   typedef std::vector<Ptr> Vector;

protected:
   Token::type_t _visibility;
   bool _constructor;
   ASTIdentifier::Ptr _name;
   ASTTypeIdentifier::VectorPtr _params;
   ASTTypeIdentifier::VectorPtr _results;
   ASTType::Vector _throws;
   ASTCall::Ptr _extends;
   ASTBlock::Ptr _body;

public:
   ASTMethod( const Token& visibility );

   virtual bool constructor() const { return _constructor; }
   virtual void set_constructor( bool constructor );
   virtual const ASTIdentifier::Ptr identifier() const { return _name; }
   virtual void set_identifier( ASTIdentifier::Ptr id );
   virtual void set_params( ASTTypeIdentifier::VectorPtr params );
   virtual void set_results( ASTTypeIdentifier::VectorPtr results );
   virtual void add_throws( ASTType::Ptr throws );
   virtual void set_extends( ASTCall::Ptr extends );
   virtual const ASTBlock::Ptr body() const { return _body; }
   virtual void set_body( ASTBlock::Ptr body );
   virtual void print( uint32_t level ) const;
};

class ASTGeneric : public AST
{
public:
   typedef boost::shared_ptr<ASTGeneric> Ptr;
   typedef std::vector<Ptr> Vector;

protected:
   std::string _name;
   ASTType::Vector _constraints;

public:
   ASTGeneric( const Token& name );

   virtual const std::string& name() const { return _name; }
   virtual void add_constraint( ASTType::Ptr constraint ) { _constraints.push_back( constraint ); }
   virtual void print( uint32_t level ) const;
};

class ASTEnum : public ASTTypeDefinition
{
public:
   typedef boost::shared_ptr<ASTEnum> Ptr;
   typedef std::vector<Ptr> Vector;

protected:
   ASTIdentifier::Vector _list;

public:
   ASTEnum( const Token& name ) : ASTTypeDefinition( name ) {}

   virtual uint32_t count() const { return _list.size(); }
   virtual ASTIdentifier::Ptr get( uint32_t i ) { return _list.at( i ); }
   virtual void append( ASTIdentifier::Ptr id ) { _list.push_back( id ); }
   virtual void print( uint32_t level ) const;

   virtual void shadow_check( Errors& errors );

protected:
   virtual void shadow_chain( ASTEnum& descendent, Errors& errors );
};

class ASTClass : public ASTTypeDefinition
{
public:
   typedef boost::shared_ptr<ASTClass> Ptr;
   typedef std::vector<Ptr> Vector;

public:
   typedef enum
   {
      PHYLUM_UNKNOWN,
      PHYLUM_CLASS,
      PHYLUM_IMMUTABLE,
      PHYLUM_INTERFACE
   } phylum_t;

protected:
   phylum_t _phylum;
   ASTGeneric::Vector _generics;
   ASTType::Vector _implements;
   ASTLiteral::Vector _literals;
   ASTMember::Vector _members;
   ASTMethod::Vector _methods;

public:
   ASTClass( phylum_t phylum, const Token& name );

   virtual ASTGeneric::Ptr generic( const std::string& name );
   virtual void add_generic( ASTGeneric::Ptr generic );
   virtual void add_implements( ASTType::Ptr interface );
   virtual void add_literal( ASTLiteral::Ptr literal );
   virtual void add_member( ASTMember::Ptr member );
   virtual void add_method( ASTMethod::Ptr method );
   virtual void print( uint32_t level ) const;

   virtual void shadow_check( Errors& errors );
   virtual void type_check( Module& module, Errors& errors );

protected:
   virtual void shadow_chain( ASTClass& descendent, Errors& errors );
};

template<class T>
void print_vector( T& t, uint32_t level )
{
   typename T::const_iterator i = t.begin();
   typename T::const_iterator end = t.end();

   for( ; i != end; ++i )
   {
      (*i)->print( level );
   }
}

template<class T>
void print_vectorptr( T& t, uint32_t level )
{
   if( t )
   {
      print_vector( *t, level );
   }
}

#endif // AST_H
